//
// Created by Administrator on 2017/6/1 0001.
//

#include "API.h"
#include "RecordManager.h"
#include "CatalogManager.h"
#include "IndexManager.h"
#include "Attribute.h"
#include <cstdio>
#include <set>
#include <vector>
#include <map>
#include <algorithm>
#include "test_data.h"
#define BUCKET_FULL 20
#define UNKNOWN_FILE 8
#define TABLE_FILE 9
#define INDEX_FILE 10

using namespace std;

CatalogManager *cm;
IndexManager* im;

void API::tableDrop(string tableName)
{
	if (!tableExist(tableName))
	{
		return;
	}

	vector<string> indexNameVector;

	indexNameListGet(tableName, &indexNameVector);
	for (int i = 0; i < indexNameVector.size(); i++)
	{
		printf("%s", indexNameVector[i].c_str());
        Table::text->append(QString::fromStdString(indexNameVector[i]));
        indexDrop(indexNameVector[i]);
	}

	if (Record_Manager_Pointer->tableDrop(tableName))
	{
		Catalog_Manager_Pointer->dropTable(tableName);
		printf("Drop table %s successfully\n", tableName.c_str());
        string temp = "Drop table ";
        temp += tableName;
        temp += " successfully";
        Table::text->append(QString::fromStdString(temp));
	}
}

void API::indexDrop(string indexName)
{
	if (Catalog_Manager_Pointer->findIndex(indexName) != INDEX_FILE)
	{
		printf("There is no index %s \n", indexName.c_str());
        string temp = "There is no index ";
        temp += indexName;
        Table::text->append(QString::fromStdString(temp));
		return;
	}
	if (Record_Manager_Pointer->indexDrop(indexName))
	{

		//get type of index
		int indexType = Catalog_Manager_Pointer->getIndexType(indexName);
		if (indexType == -2)
		{
			printf("ERROR\n");
            Table::text->append("ERROR");
			return;
		}
		Catalog_Manager_Pointer->dropIndex(indexName);
		Index_Manager_Pointer->dropIndex(Record_Manager_Pointer->indexFileNameGet(indexName), indexType);
//		printf("Drop index %s successfully\n", indexName.c_str());
        cout << "Drop index " << indexName << " successfully\n" << endl;
        string temp ="Drop index ";
        temp += indexName;
        temp += " successfully";
//        Table::text->append(QString::fromStdString(temp));
	}
}

void API::indexCreate(string indexName, string tableName, string attributeName)
{
	if (Catalog_Manager_Pointer->findIndex(indexName) == INDEX_FILE)
	{
		cout << "There is index " << indexName << " already" << endl;
        string temp = "There is index ";
        temp +=  indexName;
        temp += " already";
//        Table::text->append(QString::fromStdString(temp));
		return;
	}

	if (!tableExist(tableName)) return;

	vector<Attribute> attributeVector;
	Catalog_Manager_Pointer->attributeGet(tableName, &attributeVector);
	int i;
	int type = 0;
	for (i = 0; i < attributeVector.size(); i++)
	{
		if (attributeName == attributeVector[i].name)
		{
			if (!attributeVector[i].ifUnique)
			{
				cout << "the attribute is not unique" << endl;
//                Table::text->append("the attribute is not unique");

				return;
			}
			type = attributeVector[i].type;
			break;
		}
	}

	if (i == attributeVector.size())
	{
		cout << "there is not this attribute in the table" << endl;
//        Table::text->append("there is not this attribute in the table");
		return;
	}
	if (Record_Manager_Pointer->indexCreate(indexName))
	{
		Catalog_Manager_Pointer->addIndex(indexName, tableName, attributeName, type);
		// add a new index information
		int indexType = Catalog_Manager_Pointer->getIndexType(indexName);
		// get the type of index
		if (indexType == -2)
		{
			cout << "ERROR";
//            Table::text->append("ERROR");
			return;
		}
		// index type is not normal type
		Index_Manager_Pointer->createIndex(Record_Manager_Pointer->indexFileNameGet(indexName), indexType);
		// create a index tree
		Record_Manager_Pointer->indexRecordAllAlreadyInsert(tableName, indexName);
		// insert all already record into index
//		printf("Create index %s successfully\n", indexName.c_str());
        cout << "Create index " << indexName <<  " successfully" << endl;
        string temp = "Create index ";
        temp +=  indexName;
        temp += " successfully";
//        Table::text->append(QString::fromStdString(temp));
	}
	else
	{
		cout << "Create index " << indexName << " fail" << endl;
	}
}

void API::tableCreate(string tableName, vector<Attribute>* attributeVector, string primaryKeyName, int primaryKeyLocation)
{
	if (Catalog_Manager_Pointer->findTable(tableName) == TABLE_FILE)
	{
		cout << "There is a table " << tableName << " already" << endl;
        string temp ="There is a table ";
        temp += tableName;
        temp += " already";
//        Table::text->append(QString::fromStdString( temp));
		return;
	}

	// create a table file
	if (Record_Manager_Pointer->tableCreate(tableName))
	{
		// create a table information
		Catalog_Manager_Pointer->addTable(tableName, attributeVector, primaryKeyName, primaryKeyLocation);

        //printf("Create table %s successfully\n", tableName.c_str());
//        printf("Create table successfully\n");
        cout << "Create table" << tableName << " successfully" << endl;
        string temp = "Create table ";
        temp += tableName;
        temp += " successfully";
//        Table::text->append(QString::fromStdString(temp));
	}

	if (primaryKeyName != "")
	{
		// get a primary key
		string indexName = primaryIndexNameGet(tableName);
        cout << "Create index: " << indexName << " on primary key: " << primaryKeyName << endl;
		indexCreate(indexName, tableName, primaryKeyName);
	}
}

void API::recordShow(string tableName, vector<string>* attributeNameVector)
{
	vector<Condition> conditionVector;
	//recordShow(tableName, attributeNameVector, &conditionVector);
	recordShow(tableName, attributeNameVector, NULL);
}

void API::recordShow(string tableName, vector<string>* attributeNameVector, vector<Condition>* conditionVector)
{
	if (Catalog_Manager_Pointer->findTable(tableName) == TABLE_FILE)
	{
		int num = 0;
		vector<Attribute> attributeVector;
		attributeGet(tableName, &attributeVector);

		vector<string> allAttributeName;
		if (attributeNameVector == NULL) 
		{
			for (Attribute attribute : attributeVector)
			{
				allAttributeName.insert(allAttributeName.end(), attribute.name);
			}

			attributeNameVector = &allAttributeName;
		}
		tableAttributePrint(attributeNameVector);
		// print attribute name
		for (string name : *attributeNameVector)
		{
			int i = 0;
			for (i = 0; i < attributeVector.size(); i++)
			{
				if (attributeVector[i].name == name)
				{
					break;
				}
			}

			if (i == attributeVector.size())
			{
				cout << "the attribute which you want to print is not exist in the table" << endl;
//                Table::text->append("the attribute which you want to print is not exist in the table");
				return;
			}
		}
		int blockOffset = -1; // initial block offset
        string indexName;
        string attributeHasIndex;
        int keyType;
        string key;

        // find if has index
        bool ifFindIndex = false;
        for (Attribute attribute : attributeVector)
        {
            for (Condition cond : *conditionVector)
            {
                if (attribute.name == cond.attributeName && attribute.indexNameGet() != "")
                {
                    ifFindIndex = true;
                    indexName = attribute.indexNameGet();
                    keyType = attribute.typeGet();
                    attributeHasIndex = cond.attributeName;
                    key = cond.value;
                    break;
                }
                if (ifFindIndex)
                    break;
            }
        }

        // if has index
        if (ifFindIndex)
        {
            void *indexList;
            if (keyType == Attribute::TYPE_INT)
            {
                indexList = (TreeNode<int>*)Index_Manager_Pointer->getIndexList(Record_Manager_Pointer->indexFileNameGet(indexName), keyType);
                TreeNode<int>* cur = (TreeNode<int>*)indexList;
                while (cur != NULL)
                {
                    for (int i = 0; i < cur->keys.size(); i++)
                    {
                        key = cur->keys[i];

                        cout << "Using index " << indexName << " on attribute: " << attributeHasIndex << endl;
                        blockOffset = Index_Manager_Pointer->searchIndex(Record_Manager_Pointer->indexFileNameGet(indexName), key, keyType);
                        num = Record_Manager_Pointer->recordBlockShow(tableName, attributeNameVector, conditionVector, blockOffset);
                    }
                    cur = cur->nextLeafNode;
                }
            }
            else if (keyType == Attribute::TYPE_FLOAT)
            {
                indexList = (TreeNode<string>*)Index_Manager_Pointer->getIndexList(Record_Manager_Pointer->indexFileNameGet(indexName), keyType);
                TreeNode<float>* cur = (TreeNode<float>*)indexList;
                while (cur != NULL)
                {
                    for (int i = 0; i < cur->keys.size(); i++)
                    {
                        key = cur->keys[i];
                        cout << "Using index " << indexName << " on attribute: " << attributeHasIndex << endl;
                        blockOffset = Index_Manager_Pointer->searchIndex(Record_Manager_Pointer->indexFileNameGet(indexName), key, keyType);
                        num = Record_Manager_Pointer->recordBlockShow(tableName, attributeNameVector, conditionVector, blockOffset);
                    }
                    cur = cur->nextLeafNode;
                }
            }
            else // string
            {
                indexList = (TreeNode<string>*)Index_Manager_Pointer->getIndexList(Record_Manager_Pointer->indexFileNameGet(indexName), keyType);
                TreeNode<string>* cur = (TreeNode<string>*)indexList;
                set<int> boses;
                cout << "Using index " << indexName << " on attribute: " << attributeHasIndex << endl;
                while (cur != NULL)
                {
                    for (int i = 0; i < cur->count; i++)
                    {
                        key = cur->keys[i];

                        blockOffset = Index_Manager_Pointer->searchIndex(Record_Manager_Pointer->indexFileNameGet(indexName), key, keyType);
                        boses.insert(blockOffset);
                    }
                    cur = cur->nextLeafNode;
                }
                set<int>::iterator it;
                for (it = boses.begin(); it != boses.end(); it++)
                {
                    num += Record_Manager_Pointer->recordBlockShow(tableName, attributeNameVector, conditionVector, *it);
                }
            }
        }
        // No index
        else
        {
            num = Record_Manager_Pointer->recordAllShow(tableName, attributeNameVector, conditionVector);
        }

//		printf("%d records selected\n", num);
        cout << num << " records selected" << endl;
        string temp = to_string(num);
        temp += " records selected";
        //Table::text->append(QString::fromStdString(temp));
	}
	else
	{
		cout << "There is no table " << tableName << endl;
        string temp = "There is no table ";
        temp += tableName;
//        Table::text->append(QString::fromStdString(temp));
	}
}

void API::recordShow(string tableName, vector<string>* attributeNameVector, string key, string attribName)
{
    if (Catalog_Manager_Pointer->findTable(tableName) == TABLE_FILE)
    {
        int num = 0;
        vector<Attribute> attributeVector;
        attributeGet(tableName, &attributeVector);

        vector<string> allAttributeName;
        if (attributeNameVector == NULL)
        {
            for (Attribute attribute : attributeVector)
            {
                allAttributeName.insert(allAttributeName.end(), attribute.name);
            }

            attributeNameVector = &allAttributeName;
        }
        tableAttributePrint(attributeNameVector);
        // print attribute name
        for (string name : *attributeNameVector)
        {
            int i = 0;
            for (i = 0; i < attributeVector.size(); i++)
            {
                if (attributeVector[i].name == name)
                {
                    break;
                }
            }

            if (i == attributeVector.size())
            {
                cout << "the attribute which you want to print is not exist in the table" << endl;
//                Table::text->append("the attribute which you want to print is not exist in the table");
                return;
            }
        }
        int blockOffset = -1; // initial block offset

        // find index on specific attribute
        string indexName;
        int keyType;

        for (Attribute a : attributeVector)
        {
            if (a.attribNameGet() == attribName)
            {
                indexName = a.indexNameGet();
                keyType = a.typeGet();

                break;
            }
        }

        if (indexName != "")
        {
            if (Catalog_Manager_Pointer->findIndex(indexName) == INDEX_FILE)
            {
                cout << "Using index " << indexName << " on attribute: " << attribName << endl;
                blockOffset = Index_Manager_Pointer->searchIndex(Record_Manager_Pointer->indexFileNameGet(indexName), key, keyType);
            }
        }
//        cout << "Block offset: " << blockOffset << endl;
        vector<Condition> conditionVector;
        conditionVector.push_back(Condition(attribName, key, 0));
        if (blockOffset == -1)
        {
            num = Record_Manager_Pointer->recordAllShow(tableName, attributeNameVector, &conditionVector);
        }
        else
        {
            num = Record_Manager_Pointer->recordBlockShow(tableName, attributeNameVector, &conditionVector, blockOffset);
            // find the block by index,search in the block
        }

//		printf("%d records selected\n", num);
        cout << num << " records selected" << endl;
        string temp = to_string(num);
        temp += " records selected";
        //Table::text->append(QString::fromStdString(temp));
    }
    else
    {
        cout << "There is no table " << tableName << endl;
        string temp = "There is no table ";
        temp += tableName;
//        Table::text->append(QString::fromStdString(temp));
    }
}

void API::recordInsert(string tableName, vector<string>* recordContent)
{
	if (!tableExist(tableName))
	{
		return;
	}

	string indexName;
	vector<Attribute> attributeVector;
	vector<Condition> conditionVector;
	attributeGet(tableName, &attributeVector);
	for (int i = 0; i < attributeVector.size(); i++)
	{
		indexName = attributeVector[i].index;
		if (indexName != "")
		{
			int blockoffest = Index_Manager_Pointer->searchIndex(Record_Manager_Pointer->indexFileNameGet(indexName), (*recordContent)[i], attributeVector[i].type);

			if (blockoffest != -1)
			{
				cout << "insert fail because index value exist" << endl;
//                Table::text->append("insert fail because index value exist");
				return;
			}
		}
		else if (attributeVector[i].ifUnique)
		{
			Condition condition(attributeVector[i].name, (*recordContent)[i], Condition::OPERATOR_EQUAL);
			conditionVector.insert(conditionVector.end(), condition);
		}
	}

	if (conditionVector.size() > 0)
	{
		for (int i = 0; i < conditionVector.size(); i++)
		{
			vector<Condition> conditionTmp;
			conditionTmp.insert(conditionTmp.begin(), conditionVector[i]);

			int recordConflictNum = Record_Manager_Pointer->recordAllFind(tableName, &conditionTmp);
			if (recordConflictNum > 0)
			{
				cout << "insert fail because unique value exist" << endl;
//                Table::text->append("insert fail because unique value exist");
				return;
			}

		}
	}
	char recordString[2000];
	memset(recordString, 0, 2000);
	// get the record string
	Catalog_Manager_Pointer->recordStringGet(tableName, recordContent, recordString);
	// insert the record into file; and get the position of block being insert
	int recordSize = Catalog_Manager_Pointer->calcuteLenth(tableName);
	int blockOffset = Record_Manager_Pointer->recordInsert(tableName, recordString, recordSize);

	if (blockOffset >= 0)
	{
		recordIndexInsert(recordString, recordSize, &attributeVector, blockOffset);
		Catalog_Manager_Pointer->insertRecord(tableName, 1);
        cout << "insert record into table " << tableName << " successful\n" << endl;
//		printf("insert record into table %s successful\n", tableName.c_str());
        string temp = "insert record into table ";
        temp += tableName;
        temp += " successful";
//        Table::text->append(QString::fromStdString(temp));
	}
	else
	{
		cout << "insert record into table " << tableName << " fail" << endl;
        string temp = "insert record into table ";
        temp += tableName;
        temp += " fail";
//        Table::text->append(QString::fromStdString(temp));
	}
}

void API::recordDelete(string tableName)
{
	vector<Condition> conditionVector;
	recordDelete(tableName, &conditionVector);
}

void API::recordDelete(string tableName, vector<Condition>* conditionVector)
{
	if (!tableExist(tableName)) return;

	int num = 0;
	vector<Attribute> attributeVector;
	attributeGet(tableName, &attributeVector);

	int blockOffset = -1;
	if (conditionVector != NULL)
	{
		for (Condition condition : *conditionVector)
		{
			if (condition.operate == Condition::OPERATOR_EQUAL)
			{
				for (Attribute attribute : attributeVector)
				{
					if (attribute.index != "" && attribute.name == condition.attributeName)
					{
						blockOffset = Index_Manager_Pointer->searchIndex(Record_Manager_Pointer->indexFileNameGet(attribute.index), condition.value, attribute.type);
					}
				}
			}
		}
	}


	if (blockOffset == -1)
	{
		//if we con't find the block by index,we need to find all block
		num = Record_Manager_Pointer->recordAllDelete(tableName, conditionVector);
	}
	else
	{
		//find the block by index,search in the block
		num = Record_Manager_Pointer->recordBlockDelete(tableName, conditionVector, blockOffset);
	}

	//delete the number of record in in the table
	Catalog_Manager_Pointer->deleteValue(tableName, num);
//	printf("delete %d record in table %s\n", num, tableName.c_str());
    cout << "delete " << num << " record in table " << tableName << endl;
    string temp = "delete ";
    temp += to_string(num);
    temp += " record in table ";
    temp += tableName;
//    Table::text->append(QString::fromStdString(temp));
}

int API::recordNumGet(string tableName)
{
	if (!tableExist(tableName)) return 0;

	return Catalog_Manager_Pointer->getRecordNum(tableName);
}

int API::recordSizeGet(string tableName)
{
	if (!tableExist(tableName)) return 0;

	return Catalog_Manager_Pointer->calcuteLenth(tableName);
}

int API::typeSizeGet(int type)
{
	return Catalog_Manager_Pointer->calcuteLenth2(type);
}

int API::indexNameListGet(string tableName, vector<string>* indexNameVector)
{
	if (!tableExist(tableName)) 
	{
		return 0;
	}
	return Catalog_Manager_Pointer->indexNameListGet(tableName, indexNameVector);
}

void API::allIndexAddressInfoGet(vector<IndexInformation> *indexNameVector)
{
    cout << __FILE__ << ": " << __LINE__ << endl;
	Catalog_Manager_Pointer->getAllIndex(indexNameVector);
    cout << __FILE__ << ": " << __LINE__ << endl;
	for (int i = 0; i < (*indexNameVector).size(); i++)
	{
		(*indexNameVector)[i].indexName = Record_Manager_Pointer->indexFileNameGet((*indexNameVector)[i].indexName);
	}
}

int API::attributeGet(string tableName, vector<Attribute>* attributeVector)
{
	if (!tableExist(tableName)) 
	{
		return 0;
	}
	return Catalog_Manager_Pointer->attributeGet(tableName, attributeVector);
}

void API::recordIndexInsert(char* recordBegin, int recordSize, vector<Attribute>* attributeVector, int blockOffset)
{
	char* contentBegin = recordBegin;
	for (int i = 0; i < (*attributeVector).size(); i++)
	{
		int type = (*attributeVector)[i].type;
		int typeSize = typeSizeGet(type);
		if ((*attributeVector)[i].index != "")
		{
			indexInsert((*attributeVector)[i].index, contentBegin, type, blockOffset);
		}

		contentBegin += typeSize;
	}
}

void API::indexInsert(string indexName, char* contentBegin, int type, int blockOffset)
{
	string content = "";
	stringstream tmp;
	//if the attribute has index
	if (type == Attribute::TYPE_INT)
	{
		int value = *((int*)contentBegin);
		tmp << value;
	}
	else if (type == Attribute::TYPE_FLOAT)
	{
		float value = *((float*)contentBegin);
		tmp << value;
	}
	else
	{
		char value[255];
		memset(value, 0, 255);
//		memcpy(value, contentBegin, sizeof(type));
        memcpy(value, contentBegin, type);
		string stringTmp = value;
		tmp << stringTmp;
	}
	tmp >> content;
	Index_Manager_Pointer->insertIndex(Record_Manager_Pointer->indexFileNameGet(indexName), content, blockOffset, type);
}

void API::recordIndexDelete(char* recordBegin, int recordSize, vector<Attribute>* attributeVector, int blockOffset)
{
	char* contentBegin = recordBegin;
	for (int i = 0; i < (*attributeVector).size(); i++)
	{
		int type = (*attributeVector)[i].type;
		int typeSize = typeSizeGet(type);

		string content = "";
		stringstream tmp;

		if ((*attributeVector)[i].index != "")
		{
			if (type == Attribute::TYPE_INT)
			{
				int value = *((int*)contentBegin);
				tmp << value;
			}
			else if (type == Attribute::TYPE_FLOAT)
			{
				float value = *((float*)contentBegin);
				tmp << value;
			}
			else
			{
				char value[255];
				memset(value, 0, 255);
				memcpy(value, contentBegin, sizeof(type));
				string stringTmp = value;
				tmp << stringTmp;
			}
			tmp >> content;
			Index_Manager_Pointer->deleteIndexByKey(Record_Manager_Pointer->indexFileNameGet((*attributeVector)[i].index), content, type);
		}
		contentBegin += typeSize;
	}

}

int API::tableExist(string tableName)
{
	if (Catalog_Manager_Pointer->findTable(tableName) != TABLE_FILE)
	{
		cout << "There is no table " << tableName << endl;
        string temp = "There is no table ";
        temp += tableName;
//        Table::text->append(QString::fromStdString(temp));
		return 0;
	}
	else
	{
		return 1;
	}
}

string API::primaryIndexNameGet(string tableName)
{
	return  "PRIMARY_" + tableName;
}

void API::tableAttributePrint(vector<string>* attributeNameVector)
{
	int i = 0;
    string temp = "";
	for (i = 0; i < (*attributeNameVector).size(); i++)
	{
		printf("%s ", (*attributeNameVector)[i].c_str());
        temp += (*attributeNameVector)[i];
        temp += " ";
	}
    temp += "\n";
//    Table::text->append(QString::fromStdString(temp));
    //Table::text->append(" ");
	if (i != 0)
	{
		printf("\n");
	}
}

int API::recordCartesianJoinAndShow(string LtableName, string RtableName)
{
    int count = Record_Manager_Pointer->recordCartesianJoinAndShow(LtableName, RtableName);
    cout << count << " records select." << endl;
}

int API::recordJoin(string LtableName, string RtableName, vector<Nation> lt, vector<Region> rt)
{
    int count;

    // nest loop
    for (int i = 0; i < lt.size(); i++)
    {
        for (int j = 0; j < rt.size(); j++)
        {
            if (lt[i].region_key == rt[j].region_key)
            {
                cout << lt[i].nation_key << " " << lt[i].name << " " << rt[j].name << " " << rt[j].region_key << endl;
            }
            count++;
        }
    }

    return count;
}

int API::recordJoin(string LtableName, string RtableName, string TtableName, vector<Nation> lt, vector<Region> rt, vector<Customer> tt)
{
    int count;

    vector<Attribute> TattributeVector;
    attributeGet(TtableName, &TattributeVector);

    vector<string> TallAttributeName;

    for (Attribute attribute : TattributeVector)
    {
        TallAttributeName.insert(TallAttributeName.end(), attribute.name);
    }
    // nest loop
    for (int k = 0; k < tt.size(); k++)
    {
    for (int i = 0; i < lt.size(); i++)
    {
        for (int j = 0; j < rt.size(); j++)
        {
            if (lt[i].region_key == rt[j].region_key && tt[k].nation_key == lt[i].nation_key)
            {
                cout << tt[k].name << " " << lt[i].nation_key << " " << lt[i].name << " " << rt[j].name << " " << rt[j].region_key << endl;
            }
            count++;
        }
    }
    }
    return count;
}

int API::recordNotEqualJoin(string LtableName, string RtableName, vector<Nation> lt, vector<Region> rt)
{
    int count;

    if (Catalog_Manager_Pointer->findTable(LtableName) == TABLE_FILE &&
            Catalog_Manager_Pointer->findTable(RtableName) == TABLE_FILE)
    {
        int num = 0;
        vector<Attribute> LattributeVector;
        attributeGet(LtableName, &LattributeVector);

        vector<string> LallAttributeName;

        for (Attribute attribute : LattributeVector)
        {
            LallAttributeName.insert(LallAttributeName.end(), attribute.name);
        }


        vector<Attribute> RattributeVector;
        attributeGet(RtableName, &RattributeVector);

        vector<string> RallAttributeName;

        for (Attribute attribute : RattributeVector)
        {
            RallAttributeName.insert(RallAttributeName.end(), attribute.name);
        }


        // nest loop
        cout << LallAttributeName[0] << " " << LallAttributeName[1] << " " << LallAttributeName[2] << " " << RallAttributeName[1] << " " << RallAttributeName[0] << endl;
        for (int i = 0; i < lt.size(); i++)
        {
            for (int j = 0; j < rt.size(); j++)
            {
                if (lt[i].region_key != rt[j].region_key)
                {
                    cout << lt[i].nation_key << " " << lt[i].name << " "<< lt[i].region_key << " " << rt[j].name << " " << rt[j].region_key << endl;
                }
                count++;
            }
        }
    }
    return count;
}

int API::recordHashJoin(string LtableName, string RtableName, vector<Nation> lt, vector<Region> rt)
{
    int count = 0;
    if (Catalog_Manager_Pointer->findTable(LtableName) == TABLE_FILE &&
            Catalog_Manager_Pointer->findTable(RtableName) == TABLE_FILE)
    {
        int num = 0;
        vector<Attribute> LattributeVector;
        attributeGet(LtableName, &LattributeVector);

        vector<string> LallAttributeName;

        for (Attribute attribute : LattributeVector)
        {
            LallAttributeName.insert(LallAttributeName.end(), attribute.name);
        }


        vector<Attribute> RattributeVector;
        attributeGet(RtableName, &RattributeVector);

        vector<string> RallAttributeName;

        for (Attribute attribute : RattributeVector)
        {
            RallAttributeName.insert(RallAttributeName.end(), attribute.name);
        }

        ////////////////// FIRST PASS /////////////////////
        // create hash map
        map<int, Region> rtable;
        // save records in region by hash into bucket
        for (int i = 0; i < rt.size(); i++)
        {
            rtable.insert(pair<int, Region>(rt[i].region_key, rt[i]));

            // if full write this bucket to disk
            if (rtable.size() == BUCKET_FULL)
            {
                map<int, Region>::iterator it;
                for (it = rtable.begin(); it != rtable.end(); it++)
                {
                    writeBucket(it->first, it->second.toString());
                }
            }
        }

        // write bucket which is not empty to disk
        for (int i = 0; i < rtable.size(); i++)
        {
            writeBucket(i, rtable.at(i).toString());
        }

        ////////////////// SECOND PASS /////////////////////
        // nest loop
        readAllBucket();
        cout << LallAttributeName[0] << " " << LallAttributeName[1] << " " << LallAttributeName[2] << " " << RallAttributeName[1] << " " << RallAttributeName[0] << endl;
        vector<Nation>::iterator it;
        for (it =  lt.begin(); it != lt.end(); it++)
        {
            map<int, Region>::iterator iter = rtable.find(it->region_key);
            if (iter != rtable.end())
            {
                cout << it->nation_key << " " << it->name << " "<< it->region_key << " " << iter->second.name << " " << iter->second.region_key << endl;
                count++;
            }

        }
    }
    cout << count << " records select" << endl;
    return count;
}

int API::recordMergeSortJoin(string LtableName, string RtableName, vector<Nation> lt, vector<Region> rt)
{
    int num = 0;
    vector<Attribute> LattributeVector;
    attributeGet(LtableName, &LattributeVector);

    vector<string> LallAttributeName;

    for (Attribute attribute : LattributeVector)
    {
        LallAttributeName.insert(LallAttributeName.end(), attribute.name);
    }


    vector<Attribute> RattributeVector;
    attributeGet(RtableName, &RattributeVector);

    vector<string> RallAttributeName;

    for (Attribute attribute : RattributeVector)
    {
        RallAttributeName.insert(RallAttributeName.end(), attribute.name);
    }
    cout << LallAttributeName[0] << " " << LallAttributeName[1] << " " << LallAttributeName[2] << " " << RallAttributeName[1] << " " << RallAttributeName[0] << endl;

    vector<Nation> sortedNations;
    vector<Region> sortedRegions;

    // merge-sort for lt and rt
    MergeSortVector(lt, rt, sortedNations, sortedRegions);
    int NSize = sortedNations.size();
    int count = 0;
    for (;;)
    {
        if (!sortedRegions.empty() && !sortedNations.empty() && (sortedNations.back().region_key == sortedRegions.back().region_key))
        {
            cout << sortedNations.back().nation_key << " " << sortedNations.back().name << " " << sortedNations.back().region_key << " " << sortedRegions.back().name << " " << sortedRegions.back().region_key << endl;
            sortedNations.pop_back();

            count++;
        }
        else if (sortedRegions.empty() || sortedNations.empty())
        {
            break;
        }
        else
        {
            sortedRegions.pop_back();
        }

//        cout << sortedNations[i].nation_key << " " << sortedNations[i].name << endl;
    }
//    cout << sortedNations.size() << endl;
    cout << count << " records select" << endl;
}

void API::Merge(int arr[], int start, int mid, int end)
{
    int LeftLen = mid - start + 1;
    int RightLen = end - mid;
    int* Left = new int[LeftLen];
    int* Right = new int[RightLen];
    int i = 0, j = 0, k = 0, f = 0;
    for (i = 0,k = start; i < LeftLen; i++, k++) {
        Left[i] = arr[k];
    }
    for (i = 0, k = mid+1; i < RightLen; i++, k++) {
        Right[i] = arr[k];
    }
    for (k = start, i = 0, j = 0; i < LeftLen & j < RightLen; k++) {
        if (Left[i] <= Right[j]) {
            arr[k] = Left[i];
            i++;
        }
        if (Left[i] > Right[j]) {
            arr[k] = Right[j];
            j++;
        }
    }
    if (i < LeftLen) {
        for (f = i; f < LeftLen; f++, k++) {
            arr[k] = Left[f];
        }
    }
    if (j < RightLen) {
        for (f = j; f < RightLen; f++,k++) {
            arr[k] = Right[f];
        }
    }
}

void API::MergeSort(int arr[], int start, int end)
{
    if (start < end) {
        int mid = (start+end)/2;
        MergeSort(arr,start, mid);
        MergeSort(arr,mid+1, end);
        Merge(arr, start, mid, end);
    }
}
bool compN(const Nation &a, const Nation &b){
    return a.region_key < b.region_key;
}

bool compR(const Region &a, const Region &b){
    return a.region_key < b.region_key;
}
void API::MergeSortVector(vector<Nation> ns, vector<Region> rs, vector<Nation> &sortedNs, vector<Region> &sortedRs)
{
    int *ns_arr = new int[ns.size()];
    int *rs_arr = new int[rs.size()];

    for (int i = 0; i < ns.size(); i++)
    {
        ns_arr[i] = ns[i].region_key;
    }
    for (int i = 0; i < rs.size(); i++)
    {
        rs_arr[i] = rs[i].region_key;
    }

    MergeSort(ns_arr, 0, ns.size() - 1);
    MergeSort(rs_arr, 0, rs.size() - 1);
    int nsSize = ns.size();
    for (int i = 0; i < nsSize; i++)
    {
        sortedNs.push_back(ns[i]);
    }
    sort(sortedNs.begin(), sortedNs.end(), compN);
    for (int i = 0; i < rs.size(); i++)
    {
        sortedRs.push_back(rs[i]);
    }
    sort(sortedRs.begin(), sortedRs.end(), compR);
}
